/*
 * Copyright the original author or authors.
 * 
 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.mozilla.org/MPL/MPL-1.1.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 package com.blitscreen.render
{
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	import com.blitscreen.camera.Camera;
	import com.blitscreen.utils.BlitUtils;
	import com.bourre.core.HashCodeFactory;	

	public class ObjectsBuffer
	{
		protected var _oDrawPass : DrawPass;
		protected var _aObjects : Array;
		protected var _aCells : Array;
		
		protected var _pCellSize : Point;
		protected var _pWholeSize : Point;
		protected var _pBufferSize : Point;
		
		protected var _oCamera : Camera;
		
		public function ObjectsBuffer ( objects : Array,
										pass : DrawPass,
										camera : Camera, 
										cellSize : Point, 
										wholeSize : Point )
		{
			_pCellSize = cellSize;
			_pWholeSize = wholeSize;
			_aObjects = objects;
			_oDrawPass = pass;
			_oCamera = camera;
			
			_aCells = new Array();
		}
		
		public function init() : void
		{
			generateCells();
			update();
		}
		
		public function update () : void
		{
			var r : Rectangle = _oCamera.getScreen();
			var a : Object = getSpritesInRectangle( r );
			
			for ( var i : String in a )
			{
				var s : BlitSprite = a[ i ] as BlitSprite;
				//BlitDebug.DEBUG( i );
				if( isInside( s, r ) )
				{
					if( !s.visible )
					{
						s.visible = true;
						_oDrawPass.add( s );
						//BlitDebug.DEBUG( "sprite added " + s );
					}
				} 
				else
				{
					if( s.visible )
					{
						s.visible = false;
						_oDrawPass.remove( s );
						//BlitDebug.DEBUG( "sprite removed " + s );
					}
				}	
			}			
		}
		
		protected function isInside ( c : BlitSprite, rect : Rectangle ) : Boolean
		{
			var x : Number = c.position.x - c.center.x;
			var y : Number = c.position.y - c.center.y;
						 
			return rect.intersects( new Rectangle( x, y, c.area.width, c.area.height ) );
		}
		
		public function generateCells () : void
		{
			_pBufferSize = new Point 	
							(
								Math.ceil( _pWholeSize.x / _pCellSize.x ),
								Math.ceil( _pWholeSize.y / _pCellSize.y )
							);
							
			var l : Number = _pBufferSize.x * _pBufferSize.y;
			
			while ( l-- )
			{
				_aCells[ l ] = new BufferCell();
			}
			
			l = _aObjects.length;
			
			while( l-- )
			{
				var s : BlitSprite = _aObjects[ l ] as BlitSprite;
				if( s )
				{
					s.visible = false;
					var c : Array = getCellsCoveredByASprite( s );
					var id : String = HashCodeFactory.getKey( s );
					for(var i : String in c )
					{
						(c[ i ] as BufferCell).objects[ id ] = s;
					}
				}
			}
		}
		
		protected function getCellsCoveredByASprite ( sprite : BlitSprite ) : Array
		{
			var bounds : Rectangle = getSpriteBounds ( sprite );
			
			var minx : Number = Math.round ( bounds.left / _pCellSize.x );
			var miny : Number = Math.round ( bounds.top / _pCellSize.y );
			var maxx : Number = Math.round ( bounds.right / _pCellSize.x );
			var maxy : Number = Math.round ( bounds.bottom / _pCellSize.y );
			
			if( minx == maxx && miny == maxy )
			{
				return new Array
				( 
					_aCells[ BlitUtils.pos2idXY ( minx, miny, _pBufferSize ) ] 
				);
			}
			else if ( minx == maxx )
			{
				return new Array
				(
					_aCells[ BlitUtils.pos2idXY ( minx, miny, _pBufferSize ) ],
					_aCells[ BlitUtils.pos2idXY ( minx, maxy, _pBufferSize ) ]
		        );
			}
			else if ( miny == maxy )
			{
				return new Array
				(
					_aCells[ BlitUtils.pos2idXY ( minx, miny, _pBufferSize ) ],
					_aCells[ BlitUtils.pos2idXY ( maxx, miny, _pBufferSize ) ]
		        );
			}
			else
			{
				return new Array
				(
					_aCells[ BlitUtils.pos2idXY ( minx, miny, _pBufferSize ) ],
					_aCells[ BlitUtils.pos2idXY ( maxx, miny, _pBufferSize ) ],
					_aCells[ BlitUtils.pos2idXY ( minx, maxy, _pBufferSize ) ],
					_aCells[ BlitUtils.pos2idXY ( maxx, maxy, _pBufferSize ) ]
		        );
			}
		}
		
		public function getSpritesInRectangle ( rect : Rectangle  ) : Object 
		{
			var minx : Number = Math.round( rect.left / _pCellSize.x );
			var miny : Number = Math.round( rect.top / _pCellSize.y );
			var maxx : Number = Math.round( rect.right / _pCellSize.x );
			var maxy : Number = Math.round( rect.bottom / _pCellSize.y );
			
			//BlitDebug.DEBUG( minx + ", " + miny + " | " + maxx + ", " + maxy );
			
			var o : Object = new Object ();
			var l : Number = _aCells.length;
			for( var x : Number = minx; x <= maxx; x++ )
			{
				for( var y : Number = miny; y <= maxy; y++ )
				{
					var index : Number = BlitUtils.pos2idXY( x, y , _pBufferSize );
					
					if( index < l && index >= 0 )
					{
						//BlitDebug.DEBUG( " buffer contains :  " + ( cells[index] as BufferCell ).objects );
						concatObject( o, ( _aCells[index] as BufferCell ).objects );
					}
				}
			}
			return o;
		}
		
		protected function concatObject( o1 : Object, o2 : Object ) : void
		{
			for( var i : String in o2 )
			{
				o1[ i ] = o2[ i ];
			}
		}
		
		protected function getSpriteBounds ( sprite : BlitSprite ) : Rectangle
		{
			return new Rectangle( sprite.position.x - sprite.center.x,
								  sprite.position.y - sprite.center.y,
								  sprite.area.width,
								  sprite.area.height );
		}
	}
}

internal class BufferCell
{
	public var objects : Object;
	
	public function BufferCell ()
	{
		objects = new Object();
	}
	public function toString() : String
	{
		return "BufferCell["+objects+"]";
	}
}